/**
 * 组件翻译器
 */
class ComponentTranslator {
  render(instance) {
    throw new Error("render method not implements ... ");
  }

  createElement(instance) {
    throw new Error("createElement method not implements ... ");
  }

  /**
   * 预处理渲染实例
   * @param {*} instance
   */
  prepareInstance(instance) {
    let element = instance.$refElement;
    if (!element) {
      element = this.createElement(instance);
      if (instance.name) {
        element.setAttribute("data-name", instance.name);
      }
      if (instance.id) {
        element.setAttribute("data-id", instance.id);
      }
      instance.$refElement = element;
    }
  }
}

/**
 * 支持树形元素渲染的转换器
 */
class TreeableComponentTanslator extends ComponentTranslator {
  /**
   * 基本渲染逻辑
   * @param {*} instance
   */
  render(instance) {
    this._appendChildren(instance);
  }

  /**
   * 追加子元素
   * @param {*} instance
   */
  _appendChildren(instance) {
    if (instance.children && instance.children.length) {
      instance.children.forEach((child) => {
        // 添加元素子元素
        if (instance.$refElement && child.$refElement) {
          instance.$refElement.appendChild(child.$refElement);
        }
      });
    }
  }
}

class TextTranslator extends ComponentTranslator {
  render(instance) {
    let { $refElement, propValues = {} } = instance;
    $refElement.innerText = propValues.value || "";
  }

  createElement(instance) {
    return document.createElement("span");
  }
}

class ImageTranslator extends ComponentTranslator {
  render(instance) {
    let { $refElement, propValues = {} } = instance;
    $refElement.src = propValues.src || "";
  }

  createElement(instance) {
    return document.createElement("img");
  }
}

class BaseContainerTranslator extends TreeableComponentTanslator {
  createElement(instance) {
    return document.createElement("div");
  }
}

/**
 * 表格渲染转换器
 */
class TableTranslator extends TreeableComponentTanslator {
  render(instance) {
    // let element = this._createElement(instance, "table");
    // instance.children.forEach((child, index) => {
    //   const childElement = child.render();
    //   element.appendChild(childElement);
    // });
    // return element;

    console.log("render table ");

    if (instance.slots.head && instance.slots.head.length) {
      const thead = document.createElement("thead");
      instance.slots.head.forEach((slotHead, index) => {
        if (slotHead.$refElement) {
          thead.appendChild(slotHead.$refElement);
        }
      });
      if (instance.$refElement) {
        instance.$refElement.appendChild(thead);
      }
    }

    if (instance.slots.body && instance.slots.body.length) {
      const tbody = document.createElement("tbody");
      instance.slots.body.forEach((slotBody, index) => {
        if (slotBody.$refElement) {
          tbody.appendChild(slotBody.$refElement);
        }
      });
      if (instance.$refElement) {
        instance.$refElement.appendChild(tbody);
      }
    }

    console.log("render table finished ... ");
  }

  createElement(instance) {
    return document.createElement("table");
  }
}

/**
 * 表格渲染转换器
 */
class TableRowTranslator extends TreeableComponentTanslator {
  render(instance) {
    console.log("render table row ");
    if (instance.children) {
      instance.children.forEach((child, index) => {
        if (child.$refElement && child.$refElement.nodeName !== "TD") {
          const td = document.createElement("td");
          td.appendChild(child.$refElement);
          child.$refElement = td;
          if (instance.$refElement) {
            this._appendChildren(instance);
          }
        }
      });
    }
    console.log("render table row finished ... ");
  }

  createElement(instance) {
    return document.createElement("tr");
  }
}

/**
 * 表格渲染转换器
 */
class TableColumnTranslator extends ComponentTranslator {
  render(instance) {
    // let element = this._createElement(instance, "table");
    // instance.children.forEach((child, index) => {
    //   const childElement = child.render();
    //   element.appendChild(childElement);
    // });
    // return element;
  }

  createElement(instance) {
    return document.createElement("table");
  }
}

/**
 * 数据表格渲染转换器
 */
class DataTableTranslator extends ComponentTranslator {
  render(instance) {
    console.log("render data table ");
  }

  createElement(instance) {
    return document.createElement("table");
  }
}

/**
 * 定义属性
 * @param {*} define
 * @returns
 */
const prop = (define) => {
  const temp = {};
  if (define.type) {
    temp.type = define.type;
  }
  if (!temp.type) {
    temp.type = "Object";
  }
  if (define.value) {
    temp.value = define.value;
  }
  return temp;
};

/**
 * 定义布局
 * @param {*} define
 * @returns
 */
const layout = (define) => {
  let temp = {
    type: define.type,
    name: define.name,
  };

  if (define.children && define.children.length) {
    // 定义后代元素
    temp.children = [];
    define.children.forEach((child) => {
      temp.children.push(layout(child));
    });
  }
  if (define.props && typeof define.props === "object") {
    // 如何绑定属性和布局关系
    temp.props = define.props;
  }

  if (define.slots && typeof define.slots === "object") {
    // 绑定插槽关系
    temp.slots = define.slots;
  }

  if (define.propsBind && typeof define.propsBind === "object") {
    // 设置绑定关系
    temp.propsBind = define.propsBind;
  }

  if (define.loop && typeof define.loop === "object") {
    // 设置可循环关系
    temp.loop = define.loop;
  }
  return temp;
};

/**
 * 定义布局槽
 * @param {*} define
 * @returns
 */
const slot = (define) => {
  let temp = {
    type: define.type,
  };
  if (define.default && define.default.length) {
    // slot default 代表了默认的slot内容
    temp.default = define.default;
  }
  if (define.reject) {
    temp.reject = define.reject;
  }
  return temp;
};

const BuiltInBaseContainerModel = {
  type: "BaseContainer",
  container: true,
  translator: "BaseContainerTranslator",
};

const BuiltInTextModel = {
  type: "Text",
  translator: "TextTranslator",
  props: {
    value: prop({ type: "String" }),
  },
};

const BuiltInImageModel = {
  type: "Image",
  translator: "ImageTranslator",
  props: {
    src: prop({ type: "String" }),
  },
};

const BuiltInTableRowModel = {
  type: "TableRow",
  translator: "TableRowTranslator",
};

const BuiltInTableModel = {
  type: "Table",
  translator: "TableTranslator",
  props: {},
  inner: [BuiltInTableRowModel],
  slots: {
    head: slot({
      name: "rows",
      type: "TableRow[]",
    }),
    rows: slot({
      name: "rows",
      type: "TableRow[]",
    }),
  },
};

const BuiltInTableColumnModel = {
  type: "TableColumn",
  translator: "TableColumnTranslator",
  props: {
    columnName: prop({ type: "String" }),
    columnValue: prop({ type: "String" }),
  },
  slots: {
    head: slot({
      type: "ComponentModel",
      default: [layout({ type: "Text", props: { value: "{./columnName}" } })],
    }),
    body: slot({
      type: "ComponentModel",
      default: [layout({ type: "Text", props: { value: "{./columnValue}" } })],
    }),
  },
};

const BuiltInDataTableModel = {
  type: "DataTable",
  translator: "DataTableTranslator",
  props: {
    data: prop({ type: "object[]" }),
  },
  inner: [BuiltInTableColumnModel],
  slots: {
    columns: slot({
      name: "columns",
      type: "TableColumn[]",
    }),
  },
};

/**
 * 组件模型
 */
class ComponentModel {}

/**
 * 组件模型 构建器
 */
class ComponentModelBuilder {
  constructor() {
    this._componentModelIndex = {};
    // 内置模型
    const builtInComponentModelList = [
      BuiltInBaseContainerModel,
      BuiltInTextModel,
      BuiltInImageModel,
      BuiltInTableColumnModel,
      BuiltInTableRowModel,
      BuiltInTableModel,
      BuiltInDataTableModel,
    ];

    // 构建内置模型
    builtInComponentModelList.forEach((model, index) => {
      this.buildModelFromDefine(model);
    });
  }

  /**
   * 加载模型
   * @param {*} modelDefine
   */
  buildModelFromDefine(modelDefine) {
    if (!modelDefine) {
      throw new Error("modelDefine is null or undefined");
    }

    let modelType = modelDefine.type;
    if (!modelType) {
      throw new Error("name of modelDefine is null or undefined");
    }
    if (!this._componentModelIndex[modelType]) {
      // TODO 先暂时忽略校验步骤
      this._componentModelIndex[modelType] = modelDefine;
    }
    console.log(
      `model ${modelType} is defined \n`,
      JSON.stringify(modelDefine, null, 2)
    );

    return modelDefine;
  }

  /**
   * 获取模型
   * @param {*} modelType
   * @returns
   */
  getModel(modelType) {
    return this._componentModelIndex[modelType];
  }
}

/**
 * 实例构建器
 */
class ComponentInstanceBuilder {
  buildInstanceFromModel(model) {
    this._instanceIndex = {};
    const instance = this._deepCopy(model);
    console.log(`instance of ${instance.name} is created `);
    this._assign(instance, null);
    return instance;
  }

  _deepCopy(obj) {
    if (obj === null || typeof obj !== "object") {
      return obj;
    }
    let copy = Array.isArray(obj) ? [] : {};
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        copy[key] = this._deepCopy(obj[key]);
      }
    }
    return copy;
  }

  /**
   * 随机分配id ,在分配过程中会建立索引关系
   *
   * @param {*} instance
   * @param {*} parent
   */
  _assign(instance, parent) {
    while (true) {
      let id = this._generateShortId(8);
      if (!this._instanceIndex[id]) {
        instance.id = id;
        if (parent) {
          instance.pId = parent.id;
        }
        this._instanceIndex[id] = instance;
        break;
      }
    }
    if (instance.children && instance.children.length) {
      instance.children.forEach((child) => {
        this._assign(child, instance);
      });
    }

    if (instance.slots) {
      for (let slotName in instance.slots) {
        let slot = instance.slots[slotName];
        this._assign(slot, instance);
      }
    }
  }

  _generateShortId(length) {
    const charset =
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_+";
    let shortId = "";
    for (let i = 0; i < length; i++) {
      const randomIndex = Math.floor(Math.random() * charset.length);
      shortId += charset[randomIndex];
    }
    return shortId;
  }
}

/**
 * 渲染器 , 用于渲染创建后的实例
 */
class ComponentInstanceRender {
  constructor(instanceBuilder, modelBuilder) {
    this._instanceBuilder = instanceBuilder;
    this._modelBuilder = modelBuilder;

    // 内置渲染转换器
    this._builtInComponentTranslator = {
      BaseContainerTranslator: new BaseContainerTranslator(),
      ImageTranslator: new ImageTranslator(),
      TextTranslator: new TextTranslator(),
      TableColumnTranslator: new TableColumnTranslator(),
      TableRowTranslator: new TableRowTranslator(),
      TableTranslator: new TableTranslator(),
      DataTableTranslator: new DataTableTranslator(),
    };

    // 基础渲染转换器
    this._baseComponentTanslator = new TreeableComponentTanslator();
  }
  render(instance, element, data) {
    const fragment = new DocumentFragment();
    instance.$refElement = fragment;
    this._renderLayout(instance, data, []);
    element.appendChild(fragment);
  }

  /**
   * 渲染布局
   * @param {*} instance
   * @param {*} parent
   * @param {*} data
   * @param {*} parentStatck
   * @returns
   */
  _renderLayout(instance, data, parentStatck) {
    console.log(`start render for ${instance.name}`);
    const model = this._modelBuilder.getModel(instance.type);
    // 设置数据
    this._assignProps(instance, model, parentStatck, data);
    // 渲染子元素
    this._renderChildrenLayout(instance, instance.children, data, parentStatck);
    // 渲染 槽元素
    this._renderSlots(instance, data, parentStatck);

    let element = null;

    let translator = this._baseComponentTanslator;
    if (model.translator) {
      translator = this._builtInComponentTranslator[model.translator];
      if (!translator) {
        throw new Error(`translator ${model.translator} is not defined`);
      }
      translator.prepareInstance(instance);
      translator.render(instance);
    }

    // else {
    //   this._baseComponentTanslator.render(instance);
    //   // if (instance.children && instance.children.length) {
    //   //   instance.children.forEach((child) => {
    //   //     // 添加元素子元素
    //   //     if (instance.$refElement && child.$refElement) {
    //   //       instance.$refElement.appendChild(child.$refElement);
    //   //     }
    //   //   });
    //   // }

    //   translator.prepareInstance(instance);
    //   translator.render(instance);
    // }

    // 预处理
    translator.prepareInstance(instance);
    // 渲染
    translator.render(instance);
    console.log(`render for ${instance.name} finished`);
  }

  /**
   * 渲染子元素
   * @param {*} parent
   * @param {*} children
   * @param {*} data
   * @param {*} parentStatck
   */
  _renderChildrenLayout(parent, children, data, parentStatck) {
    if (children && children.length) {
      parentStatck.push(parent);
      children.forEach((child) => {
        this._renderLayout(child, data, parentStatck);
      });
      parentStatck.pop();
    }
  }

  /**
   * 渲染插槽
   * @param {*} instance
   * @param {*} data
   * @param {*} parentStatck
   */
  _renderSlots(instance, data, parentStatck) {
    if (instance.slots) {
      parentStatck.push(instance);
      for (let slotName in instance.slots) {
        let slot = instance.slots[slotName];
        console.log("render slot of instance ", instance, slot);
        if (slot && slot.length) {
          slot.forEach((child) => {
            this._renderLayout(child, data, parentStatck);
          });
        }
      }
      parentStatck.pop();
    }
  }

  _assignProps(instance, model, parentStatck, data) {
    const tempProps = {};
    instance.propValues = tempProps;
    if (!model) {
      throw new Error(`the model of instance ${instance.type} is not defined`);
    }

    const props = model.props;
    const propsBind = instance.propsBind;
    if (parentStatck && parentStatck.length > 0) {
      // 如果堆栈深度小于或者等于0表示当前组件是根组件,无法采用数据绑定
      for (let propName in propsBind) {
        let propDefine = props[propName];
        if (!propDefine) {
          continue;
        }
        // TODO 跳过类型转换和数据校验
        let bind = propsBind[propName] || "";
        if (bind.indexOf("{") > -1 && bind.indexOf("}") > -1) {
          if (!parentStatck || parentStatck.length <= 0) {
            continue;
          }
          let bindExpress = bind.substring(1, bind.length - 1).trim();
          // TODO 表达式进行简单处理暂不支持动态语法

          if (bindExpress.indexOf("/") === 0) {
            // 从根路径开始取值
            let pathList = bindExpress.split("/");
            let realPathList = [];
            for (let i = 0; i < pathList.length; i++) {
              let path = pathList[i].trim();
              if (path !== "") {
                realPathList.push(path);
              }
            }
            if (realPathList.length > parentStatck.length) {
              continue;
            }
            let value = null;
            let target = parentStatck[0];
            for (let i = 0; i < realPathList.length; i++) {
              if (!target) {
                // continue inner loop
                continue;
              }
              let path = realPathList[i];
              if (i === realPathList.length - 1) {
                value = target.propValues[path];
                break;
              }
              target = target.propValues[path];
            }
            if (value) {
              tempProps[propName] = value;
            }
          }
        } else {
          tempProps[propName] = bind;
        }
      }
    } else {
      for (let propName in props) {
        let value = data[propName];
        // TODO 跳过类型转换和数据校验
        tempProps[propName] = value;
      }
    }

    console.log("instance props is assigned ", instance);
  }
}

/**
 * 编辑器,提供编辑入口
 */
class Editor {
  constructor() {
    this.modelBuilder = new ComponentModelBuilder();
    this.instanceBuilder = new ComponentInstanceBuilder();
    this.render = new ComponentInstanceRender(
      this.instanceBuilder,
      this.modelBuilder
    );
    this.rootModel = null;
    this.rootInstance = null;

    this._targetData = null;
    this._renderElement = null;
  }
  load(modelDefine) {
    this.rootModel = this.modelBuilder.buildModelFromDefine(modelDefine);
  }

  /**
   * 切换演示数据
   * @param {*} data
   */
  updateData(data) {
    this._targetData = data;
    this._render();
  }

  bind(element) {
    if (!element) {
      throw new Error("element can not be null");
    }
    if (!this.rootModel) {
      throw new Error("the model is not loaded ");
    }
    this.rootInstance = this.instanceBuilder.buildInstanceFromModel(
      this.rootModel
    );

    console.log(
      "model instance = ",
      JSON.stringify(this.rootInstance, null, 2)
    );
    this._renderElement = element;
    this._render();
  }

  _render() {
    if (!this.rootInstance || !this._renderElement) {
      return;
    }
    this.render.render(
      this.rootInstance,
      this._renderElement,
      this._targetData
    );
  }
}

// 历史数据模板
const HistoryTemplate = {
  loginTime: "",
  logoutTime: "",
  ip: "",
};

const UserProfileModel = {
  type: "UserProfile",
  props: {
    nickName: prop({ type: "String" }),
    id: prop({ type: "String" }),
    age: prop({ type: "String" }),
    properties: prop({ type: "Map[String,Object]" }),
    avator: prop({ type: "String" }),
    history: prop({ type: "History[]" }),
  },
  name: "mainComponent",
  children: [
    layout({
      name: "nickNameLabel",
      type: "Text",
      propsBind: {
        value: "{/nickName}",
      },
    }),

    layout({
      name: "innerContainer",
      type: "BaseContainer",
      children: [
        layout({
          name: "ageLabel",
          type: "Text",
          propsBind: {
            value: "{/age}",
          },
        }),

        layout({
          name: "avatorContainer",
          type: "BaseContainer",
          children: [
            layout({
              name: "avatorImage",
              type: "Image",
              propsBind: {
                src: "{/avator}",
              },
            }),
          ],
        }),

        // 基础表格
        layout({
          name: "baseLoopTable",
          type: "Table",
          slots: {
            head: [
              layout({
                type: "TableRow",
                name: "baseTableRowHead",
                children: [
                  layout({
                    type: "Text",
                    name: "propHeadNameLabel",
                    propsBind: {
                      value: "属性",
                    },
                  }),

                  layout({
                    type: "Text",
                    name: "propHeadValueLabel",
                    propsBind: {
                      value: "值",
                    },
                  }),
                ],
              }),
            ],
            body: [
              layout({
                type: "TableRow",
                loop: {
                  indexName: "index",
                  valueName: "item",
                  source: "{/properties}",
                },
                name: "loopableBaseTableRow",
                children: [
                  layout({
                    type: "Text",
                    name: "propNameLabel",
                    propsBind: {
                      value: "item/propName",
                    },
                  }),

                  layout({
                    type: "Text",
                    name: "propValueLabel",
                    propsBind: {
                      value: "item/propValue",
                    },
                  }),
                ],
              }),
            ],
          },
        }),

        // 数据表格
        layout({
          name: "baseDataTable",
          type: "DataTable",
          propsBind: {
            data: "{/history}",
          },
          slots: {
            columns: [
              layout({
                type: "TableColumn",
                name: "loginTimeColumn",
                propsBind: {
                  columnName: "loginTime",
                },
              }),

              layout({
                type: "TableColumn",
                name: "logoutTimeColumn",
                propsBind: {
                  columnName: "logoutTime",
                },
              }),

              layout({
                type: "TableColumn",
                name: "ipColumn",
                propsBind: {
                  columnName: "ip",
                },
              }),

              layout({
                type: "TableColumn",
                name: "avatorImage",
                propsBind: {
                  columnName: "avator",
                },
                slots: {
                  body: [
                    layout({
                      type: "Image",
                      name: "tableAvatorImage",
                      propsBind: {
                        src: "{row/avator}",
                      },
                    }),
                  ],
                },
              }),
            ],
          },
        }),
      ],
    }),
  ],
};
const editor = new Editor();
editor.load(UserProfileModel);
editor.updateData({
  nickName: "张三",
  id: "012345678",
  age: 18,
  avator:
    "https://img-s-msn-com.akamaized.net/tenant/amp/entityid/BB1oqqzJ.img?w=640&h=426&m=6",
  properties: {
    high: "180cm",
    weight: "70kg",
    level: "L-1",
  },
  history: [],
});

let appElement = document.getElementById("app");
editor.bind(appElement);
